import {App, HeadingCache, TFile, TFolder, Vault} from "obsidian";
import {slice} from "builtin-modules";


export default class GenerateHeadings {
  app: App;
  private upLevelHeadingSuggestions: Map<string, string[]>;
  private vaultStartLocation: string;
  private ignoredHeaders: Array<string>;
  private ignoredSuffixArray: Array<string>;

  constructor(app: App, vaultStartLocation: string, ignoredHeaders: Array<string>, ignoredSuffixArray: Array<string>) {
    this.app = app;
    this.vaultStartLocation = vaultStartLocation;
    this.ignoredHeaders = ignoredHeaders;
    this.ignoredSuffixArray = ignoredSuffixArray;
  }

  getUpLevelHeading(headingIndex: number, headingCandidates: HeadingCache[], headingChain: HeadingCache[]) {
    const currentHeading = headingCandidates[headingIndex];
    const level = currentHeading.level;
    if (level <= 2) {
      headingChain.push(currentHeading);
      return;
    }
    const upLevel = level - 1;
    for (let i = headingIndex; i--; i>= 0)
    {
      if (headingCandidates[i].level <= upLevel){ // 找到上一级heading
        headingChain.push(currentHeading);
        this.getUpLevelHeading(i, headingCandidates, headingChain);
        break;
      }
    }
  }
  getUpLevelHeadingRecursively(cachedHeadings: HeadingCache[]) : Map<HeadingCache, HeadingCache[]>{
    const upLevelHeadingsArray = cachedHeadings.map((item, i) => {
      const chain: HeadingCache[] = [];
      this.getUpLevelHeading(i, cachedHeadings, chain);
      return chain;
    });

    const upLevelHeadingsMap = new Map();
    for(const heading of upLevelHeadingsArray){
      upLevelHeadingsMap.set(heading[0], heading.slice(1));
    }
    return upLevelHeadingsMap;
  }
  getHeadings(): string[]{
    let headingSuggestions: string[] = [];

    const markdownFiles: TFile[] = [];
    const startLocation = this.vaultStartLocation.trim();
    const folderOrFile = this.app.vault.getAbstractFileByPath(startLocation);
    if (folderOrFile instanceof TFile) {
      markdownFiles.push(folderOrFile);
    } else if (folderOrFile instanceof TFolder) {
      Vault.recurseChildren(folderOrFile, (file => {
        if (file instanceof TFile && file.extension === "md") {
          markdownFiles.push(file);
        }
      }));
    }

    // const markdownFiles = this.app.vault.getMarkdownFiles();
    markdownFiles.sort((a, b) => {
      if (a.stat.ctime > b.stat.ctime) return -1;
      if (a.stat.ctime < b.stat.ctime) return 1;
      return 0;
    });

    // const upLevelHeadingSuggestions: Map<string, string> = new Map();
    this.upLevelHeadingSuggestions = new Map();
    markdownFiles.forEach((markFile: TFile) => {
      for (const ignoredSuffix of this.ignoredSuffixArray) {
        if (markFile.path.endsWith(ignoredSuffix)) {
          return null;
        }
      }
      if (this.app.metadataCache.getFileCache(markFile) && this.app.metadataCache.getFileCache(markFile).headings) {
        const cachedHeadings = this.app.metadataCache.getFileCache(markFile).headings;
        const upLevelHeadingsMap = this.getUpLevelHeadingRecursively(cachedHeadings);
        upLevelHeadingsMap.forEach((headingCacheArray, keyHeading) => {
          // headingCacheArray.push(keyHeading)
          const reversedArray = headingCacheArray.reverse();
          reversedArray.push(keyHeading)
          const upLevelHeadingsText: string[] = reversedArray.map(v => "#".repeat(v.level) + ` ${v.heading}`);
          // this.upLevelHeadingSuggestions.set("#".repeat(keyHeading.level) + ` ${keyHeading.heading}`, upLevelHeadingsText);
          this.upLevelHeadingSuggestions.set(keyHeading.heading, upLevelHeadingsText);
        });
        // console.log(upLevelHeadingsMap);
        if (cachedHeadings){
          headingSuggestions = headingSuggestions.concat(cachedHeadings.map((v) => {
            // return  "#".repeat(v.level) + ` ${v.heading} (${markFile.path})`;
            return  "#".repeat(v.level) + ` ${v.heading}`; // TODO: 最好加一个hint，但是去重要手动做
          }));
        }
      }
    });
    headingSuggestions = headingSuggestions.filter(heading => { return !this.ignoredHeaders.contains(heading); });
    return Array.from(new Set(headingSuggestions));
  }

  selectSuggestion(headingText: string, content: string): string {
    this.getHeadings();
    // const { editor } = this.context;
    let selectedHeadingText = headingText;
    // let newMap : Map<string, string[]> = new Map();
    // for (const [key, value] of this.upLevelHeadingSuggestions.entries()) {
    //   newMap.set(key.trimStart("#").trim(), value);
    // }

    const upLevelHeadingsText = this.upLevelHeadingSuggestions.get(selectedHeadingText);
    [selectedHeadingText] = upLevelHeadingsText.slice(-1);
    // const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);
    // const content = mdView.data;
    let replacementWithUpLevel = "";
    if (!upLevelHeadingsText || upLevelHeadingsText.length <= 0) {
      throw Error(`upper level heading not found: ${headingText}`);
    }
    let topMostHeadingIndex = -1;
    for (const upLevelHeadingText of upLevelHeadingsText) {
      if (content.indexOf(upLevelHeadingText) === -1) { // JM：表示现在文档中没有过这一层目录
        replacementWithUpLevel = upLevelHeadingText + "\n" + replacementWithUpLevel;
      }
      else {
        topMostHeadingIndex += 1;
      }
    }

    let topMostHeadingText: string; // 找到必须存在的最上头的heading，目的是为了找到插入定位
    if (!this.upLevelHeadingSuggestions.get(headingText)){
      throw Error(`cannot found topMost heading for ${headingText}`);
    }
    if (this.upLevelHeadingSuggestions.get(headingText).length === 0){
      topMostHeadingText = headingText;
    } else {
      topMostHeadingText = this.upLevelHeadingSuggestions.get(headingText)[topMostHeadingIndex];
    }

    // const [topMostHeadingText] = this.upLevelHeadingSuggestions.get(upLevelHeadingsText.slice(-1)[0]); // TODO: 这段逻辑是有bug的，
    console.log(`topMostHeadingText: ${topMostHeadingText} headingText: ${headingText}`);
    const startOffset = content.indexOf(topMostHeadingText) + topMostHeadingText.length;
    content = content.slice(0, startOffset).trimEnd() + `\n${replacementWithUpLevel}\n` + content.slice(startOffset).trimStart();
    return content;
    // editor.replaceRange(replacementWithUpLevel, this.context.start, this.context.end);
  }
}

